package com.gree.flowable.app.services.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gree.flowable.app.mapper.FlowableProcessInsMapper;
import com.gree.flowable.app.pojo.model.*;
import com.gree.flowable.app.pojo.vo.ProcessInstanceQueryVO;
import com.gree.flowable.app.pojo.vo.ProcessInstanceVO;
import com.gree.flowable.app.pojo.vo.ReturnVO;
import com.gree.flowable.app.pojo.vo.TaskVO;
import com.gree.flowable.app.services.BaseProcessService;
import com.gree.flowable.app.services.IFlowProcessDiagramGenerator;
import com.gree.flowable.app.services.IFlowableProcessInsService;
import com.gree.flowable.app.services.IFlowableTaskService;
import com.gree.flowable.app.tools.constants.CommentTypeEnum;
import com.gree.flowable.app.tools.constants.FlowConstant;
import com.gree.flowable.app.tools.constants.ReturnCode;
import com.gree.flowable.app.tools.utils.StringSplitUtil;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.common.engine.impl.util.IoUtil;
import org.flowable.editor.language.json.converter.util.CollectionUtils;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.idm.api.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class IFlowableProcessInsServiceImpl extends BaseProcessService implements IFlowableProcessInsService {

    @Autowired
    private IFlowProcessDiagramGenerator iFlowProcessDiagramGenerator;

    @Autowired
    private IFlowableBpmnModelServiceImpl iFlowableBpmnModelService;

    @Autowired
    private FlowableProcessInsMapper flowableProcessInsMapper;

    @Autowired
    private IFlowableTaskService iFlowableTaskService;

    @Override
    public byte[] createImage(String businessKey) {
        //1.获取当前的流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(businessKey).singleResult();
        String processInstanceId= null;
        if (processInstance == null){
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).singleResult();
            processInstanceId = historicProcessInstance.getId();
        }else {
            processInstanceId = processInstance.getId();
        }
        String processDefinitionId = null;
        List<String> activeActivityIds = new ArrayList<>(); // 高亮节点
        List<String> highLightedFlows = new ArrayList<>();  // 高亮线
        //2.获取所有的历史轨迹线对象
        List<HistoricActivityInstance> historicSquenceFlows = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId).activityType(BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW).list();
        historicSquenceFlows.forEach(historicActivityInstance -> highLightedFlows.add(historicActivityInstance.getActivityId()));
        //3. 获取流程定义id和高亮的节点id
        if (processInstance != null) {
            //3.1. 正在运行的流程实例
            processDefinitionId = processInstance.getProcessDefinitionId();
            activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);
        } else {
            //3.2. 已经结束的流程实例
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId = historicProcessInstance.getProcessDefinitionId();
            //3.3. 获取结束节点列表
            List<HistoricActivityInstance> historicEnds = historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(processInstanceId).activityType(BpmnXMLConstants.ELEMENT_EVENT_END).list();
            List<String> finalActiveActivityIds = activeActivityIds;
            historicEnds.forEach(historicActivityInstance -> finalActiveActivityIds.add(historicActivityInstance.getActivityId()));
        }
        //4. 获取bpmnModel对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        //5. 生成图片流
        InputStream inputStream = iFlowProcessDiagramGenerator.generateDiagram(bpmnModel, activeActivityIds, highLightedFlows);
        //6. 转化成byte便于网络传输
        byte[] datas = IoUtil.readInputStream(inputStream, "image inputStream name");
        return datas;
    }

    @Override
    public ReturnVO stopProcessInstanceByKey(EndProcessMO endVo) {
        ReturnVO returnVO = null;
        ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(endVo.getBusinessKey()).singleResult();
        if (instance != null) {
            // 1、添加审批记录
            this.addComment(endVo.getUserCode(), instance.getId(), CommentTypeEnum.LCZZ.toString(),
                    endVo.getMessage());
            List<EndEvent> endNodes = iFlowableBpmnModelService.findEndFlowElement(instance.getProcessDefinitionId());
            String endId = endNodes.get(0).getId();
            String processInstanceId = instance.getId();
            // 2、执行
            List<Execution> executions = runtimeService.createExecutionQuery().parentId(processInstanceId).list();
            List<String> executionIds = new ArrayList<>();
            executions.forEach(execution -> executionIds.add(execution.getId()));
            // 流程跳转
            this.moveExecutionsToSingleActivityId(executionIds, endId);
            returnVO = new ReturnVO(ReturnCode.SUCCESS, "终止成功");
        } else {
            returnVO = new ReturnVO(ReturnCode.FAIL, "不存在运行的流程实例,请确认!");
        }
        return returnVO;
    }

    @Override
    public ReturnVO<ProcessInstance> startProcessInstanceByKey(StartProcessInstanceMO params) {
        ReturnVO<ProcessInstance> returnVO = new ReturnVO<>(ReturnCode.SUCCESS, "启动成功");
        if (StringUtils.isNotBlank(params.getProcessDefinitionKey())
                && StringUtils.isNotBlank(params.getBusinessKey())) {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(params.getProcessDefinitionKey())
                    .latestVersion().singleResult();
            if (processDefinition != null && processDefinition.isSuspended()) {
                returnVO = new ReturnVO<>(ReturnCode.FAIL, "此流程已经挂起,请联系系统管理员!");
                return returnVO;
            }

            //1、当我们流程创建人和发起人
            String creator = params.getCreator();
            if (StringUtils.isBlank(creator)) {
                creator = params.getCurrentUserCode();
                params.setCreator(creator);
            }

            /**
             * 1、设置变量
             * 1.1、设置提交人字段为空字符串让其自动跳过
             * 1.2、设置可以自动跳过
             * 1.3、汇报线的参数设置
             */

            if(params.getVariables() == null){
                params.setVariables(new HashMap<String, Object>());
            }

            //2.1、设置提交人  字段为空字符串让其自动跳过
            params.getVariables().put(FlowConstant.FLOW_SUBMITTER_VAR, creator);
            //2.2、设置可以自动跳过
            params.getVariables().put(FlowConstant.FLOWABLE_SKIP_EXPRESSION_ENABLED, true);
            // TODO 2.3、汇报线的参数设置

            //3.启动流程 identityService.setAuthenticatedUserId(user)  设置当前操作处理人  这里是设置启动人
            identityService.setAuthenticatedUserId(creator);
            ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
                    .processDefinitionKey(params.getProcessDefinitionKey().trim())
                    .name(params.getFormName().trim())
                    .businessKey(params.getBusinessKey().trim())
                    .variables(params.getVariables())
                    .tenantId(processDefinition.getTenantId())
                    .start();
            returnVO.setData(processInstance);
            //4.添加审批记录
            this.addComment(params.getCurrentUserCode(), processInstance.getProcessInstanceId(),
                    CommentTypeEnum.TJ.toString(), "生成流程实例");
            //5.TODO 推送消息数据
        } else {
            returnVO = new ReturnVO<>(ReturnCode.FAIL, "请填写 这两个字段 ProcessDefinitionKey,BusinessKey");
        }
        return returnVO;
    }

    @Override
    public PagerModel<ProcessInstanceVO> getMyProcessInstances(ProcessInstanceQueryVO queryVo, Query query) {
        Page<TaskVO> page = new Page<>(query.getPageNum(), query.getPageSize());
        IPage<ProcessInstanceVO> myProcesses = flowableProcessInsMapper.getMyLaunchTaskList(page, queryVo);
        myProcesses.getRecords().forEach(processInstanceVO -> {
            this.setStateApproper(processInstanceVO);
        });
        return new PagerModel<>(myProcesses);
    }

    @Override
    public ReturnVO<String> revokeProcess(RevokeProcessMO revokeVo) {
        ReturnVO<String> returnVO = new ReturnVO<>(ReturnCode.FAIL, "撤回失败!");
        if (StringUtils.isNotBlank(revokeVo.getBusinessKey())) {
            ProcessInstance instance = runtimeService.createProcessInstanceQuery()
                    .processInstanceBusinessKey(revokeVo.getBusinessKey()).singleResult();
            if (instance != null) {
                String instanceId = instance.getId();
                //1.添加撤回意见
                this.addComment(revokeVo.getUserCode(), instanceId, CommentTypeEnum.CH.toString(), revokeVo.getMessage());
                //2.设置提交人 来到开始节点不再跳过
                runtimeService.setVariable(instanceId, FlowConstant.FLOW_SUBMITTER_VAR, instance.getStartUserId());
                //3.执行撤回 先找到开始节点
//                Activity disActivity = iFlowableBpmnModelService.findActivityByName(processInstance.getProcessDefinitionId(), FlowConstant.FLOW_SUBMITTER);
                FlowNode startNode = iFlowableBpmnModelService.findFlowNodeByActivityId(instance.getProcessDefinitionId(), FlowConstant.FLOW_SUBMIT_NODE);
                //4.删除运行和历史的节点信息
                this.deleteActivity(startNode.getId(), instanceId);
                //5.执行跳转
                List<Execution> executions = runtimeService.createExecutionQuery().parentId(instanceId).list();
                List<String> executionIds = new ArrayList<>();
                executions.forEach(execution -> executionIds.add(execution.getId()));
                this.moveExecutionsToSingleActivityId(executionIds, startNode.getId());
                returnVO = new ReturnVO<>(ReturnCode.SUCCESS, "撤回成功!");
            }
        } else {
            returnVO = new ReturnVO<>(ReturnCode.FAIL, "单号不能为空!");
        }
        return returnVO;
    }

    @Override
    public int countBusinessKey(String businessKey) {
        return flowableProcessInsMapper.countBusinessKey(businessKey);
    }

    /**
     * @param processInstanceVo 参数
     * @Title: 设置状态和审批人
     */
    private void setStateApproper(ProcessInstanceVO processInstanceVo) {
        if (processInstanceVo.getEndTime() == null) {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceVo.getProcessInstanceId())
                    .singleResult();
            if (processInstance.isSuspended()) {
                processInstanceVo.setSuspensionState(FlowConstant.SUSPENSION_STATE);
            } else {
                processInstanceVo.setSuspensionState(FlowConstant.ACTIVATE_STATE);
            }
            List<Map> assigneeAndTaskNode = iFlowableTaskService.getApproversAndTaskNode(processInstanceVo.getProcessInstanceId());
            String[] approvers = StringSplitUtil.createApprovers2(assigneeAndTaskNode);
            processInstanceVo.setApprover(approvers[0]);
            processInstanceVo.setActTaskName(approvers[1]);
        }else {
            processInstanceVo.setActTaskName("已完结");
        }
    }

    /**
     * @param approvers 审批人列表
     * @return
     * @Title: 组合审批人显示名称
     */
    private String createApprovers(List<User> approvers) {
        if (CollectionUtils.isNotEmpty(approvers)) {
            StringBuffer approverstr = new StringBuffer();
            StringBuffer finalApproverstr = approverstr;
            approvers.forEach(user -> {
                finalApproverstr.append(user.getDisplayName()).append(";");
            });
            if (approverstr.length() > 0) {
                approverstr = approverstr.deleteCharAt(approverstr.length() - 1);
            }
            return approverstr.toString();
        }
        return null;
    }
}
